package secondriver.heathfood.util.unused;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Parcel;
import android.os.Parcelable;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.UUID;

/**
 * Author : secondriver
 * Created : 2015/10/26.
 */
public class SimpleSQLiteCache {

    public final static class MetaInfo {

        private static final String CACHE_TABLE_NAME = "t_kv_cache";
        private static final String ID_COLUMN = "_id";
        private static final String KEY_COLUMN = "key_field";
        private static final String VALUE_COLUMN = "value_field";
        private static final String[] COLUMNS = {ID_COLUMN, KEY_COLUMN, VALUE_COLUMN};

        public static String[] getInitScripts() {
            String createTableSql = new StringBuilder()
                    .append("CREATE TABLE IF NOT EXISTS ")
                    .append(CACHE_TABLE_NAME)
                    .append("( ")
                    .append(ID_COLUMN)
                    .append(" INTEGER PRIMARY KEY AUTOINCREMENT , ")
                    .append(KEY_COLUMN)
                    .append(" VARCHAR , ")
                    .append(VALUE_COLUMN)
                    .append(" TEXT );").toString();
            return new String[]{createTableSql};
        }
    }

    private static CacheKeyGen defaultCacheKeyGen = new DefaultCacheKeyGen();

    private static volatile SimpleSQLiteCache simpleSQLiteCache;

    private final SQLiteOpenHelper sqLiteOpenHelper;

    private CacheKeyGen cacheKeyGen = defaultCacheKeyGen;

    private PutListener cachePutListener;

    private GetListener cacheGetListener;

    private DelListener cacheDelListener;

    public static SimpleSQLiteCache getInstance(final SQLiteOpenHelper sqLiteOpenHelper) {
        checkNotNull(sqLiteOpenHelper, "sqLiteOpenHelper");
        if (null == simpleSQLiteCache) {
            synchronized (SimpleSQLiteCache.class) {
                if (null == simpleSQLiteCache) {
                    simpleSQLiteCache = new SimpleSQLiteCache(sqLiteOpenHelper);
                }
            }
        }
        return simpleSQLiteCache;
    }

    private SimpleSQLiteCache(final SQLiteOpenHelper sqLiteOpenHelper) {
        this.sqLiteOpenHelper = sqLiteOpenHelper;
    }

    /**
     * Set global cache `get event` listener
     *
     * @param cacheGetListener
     */
    public void setCacheGetListener(GetListener cacheGetListener) {
        checkNotNull(cacheKeyGen, "cacheGetListener");
        this.cacheGetListener = cacheGetListener;
    }

    /**
     * Set global cache `put event` listener
     *
     * @param cachePutListener
     */
    public void setCachePutListener(PutListener cachePutListener) {
        checkNotNull(cacheKeyGen, "cachePutListener");
        this.cachePutListener = cachePutListener;
    }

    /**
     * Set global cache `delete event` listener
     *
     * @param cacheDelListener
     */
    public void setCacheDelListener(DelListener cacheDelListener) {
        checkNotNull(cacheDelListener, "cacheDelListener");
        this.cacheDelListener = cacheDelListener;
    }

    /**
     * Set global cache `key generator`
     *
     * @param cacheKeyGen
     */
    public void setCacheKeyGen(CacheKeyGen cacheKeyGen) {
        checkNotNull(cacheKeyGen, "cacheKeyGen");
        this.cacheKeyGen = cacheKeyGen;
    }

    private static void checkNotNull(Object obj, String message) {
        if (null == obj) {
            throw new IllegalArgumentException(message + " parameter must not null .");
        }
    }

    public boolean exist(String key) {
        SQLiteDatabase db = sqLiteOpenHelper.getReadableDatabase();
        Cursor cursor = db.rawQuery("SELECT COUNT(1) AS count FROM " + MetaInfo.CACHE_TABLE_NAME + " WHERE " + MetaInfo
                .KEY_COLUMN + "=?", new
                String[]{key});
        boolean res = cursor.getCount() >= 1;
        cursor.close();
        db.close();
        return res;
    }

    /**
     * Put a value as cache item's value
     *
     * @param value cache item's value
     * @return cache item
     */
    public KVCacheItem put(String value) {
        return put(cacheKeyGen.keyGen(), value);
    }

    /**
     * Put a key association value as kv cache's item
     *
     * @param key   key association kv cache's item
     * @param value cache item's value
     * @return cache item
     */
    public KVCacheItem put(String key, String value) {
        return put(key, value, cachePutListener);
    }

    /**
     * Put a key association value as kv cache's item
     *
     * @param key      key association kv cache's item
     * @param value    cache item's value
     * @param listener cache item put event listener
     * @return cache item
     */
    public KVCacheItem put(String key, String value, PutListener listener) {

        ContentValues cv = new ContentValues(2);
        cv.put(MetaInfo.KEY_COLUMN, key);
        cv.put(MetaInfo.VALUE_COLUMN, value);

        KVCacheItem KVCacheItem = get(key, null);

        boolean state = false;
        boolean insertOrUpdate;
        if (KVCacheItem.NULL != KVCacheItem) {
            insertOrUpdate = false;
            SQLiteDatabase db = sqLiteOpenHelper.getWritableDatabase();
            int effectRows = update(db, MetaInfo.CACHE_TABLE_NAME, key, value);
            if (effectRows == 1) {
                state = true;
                KVCacheItem.setValue(value);
            } else {
                KVCacheItem = KVCacheItem.NULL;
            }
            db.close();
        } else {
            insertOrUpdate = true;
            SQLiteDatabase db = sqLiteOpenHelper.getWritableDatabase();
            long id = db.insert(MetaInfo.CACHE_TABLE_NAME, null, cv);
            if (id != -1) {
                state = true;
                KVCacheItem = new KVCacheItem(id, key, value);
            }
            db.close();
        }
        if (listener != null) {
            listener.onPutCallback(state, insertOrUpdate, KVCacheItem);
        }
        return KVCacheItem;
    }

    /**
     * Get key association kv cache's item
     *
     * @param key cache item's key
     * @return cache item
     */
    public KVCacheItem get(String key) {
        return get(key, cacheGetListener);
    }

    /**
     * Get key association kv cache's item
     *
     * @param key      cache item's key
     * @param listener get cache item's listener
     * @return cache item
     */
    public KVCacheItem get(String key, GetListener listener) {
        SQLiteDatabase db = sqLiteOpenHelper.getWritableDatabase();
        Cursor cursor = db.query(MetaInfo.CACHE_TABLE_NAME, MetaInfo.COLUMNS, MetaInfo.KEY_COLUMN + "=?", new String[]{key}, null, null, null);
        KVCacheItem returnKVCacheItem = KVCacheItem.NULL;
        if (cursor.getCount() == 1 && cursor.moveToFirst()) {
            returnKVCacheItem = new KVCacheItem(
                    cursor.getLong(cursor.getColumnIndex(MetaInfo.ID_COLUMN)),
                    cursor.getString(cursor.getColumnIndex(MetaInfo.KEY_COLUMN)),
                    cursor.getString(cursor.getColumnIndex(MetaInfo.VALUE_COLUMN))
            );
        } else {
            if (null != listener) {
                String value = listener.onGetCallback(key);
                if (null != value) {
                    long id = insert(db, MetaInfo.CACHE_TABLE_NAME, key, value);
                    if (id != -1) {
                        returnKVCacheItem = new KVCacheItem(id, key, value);
                    }
                }
            }
        }
        cursor.close();
        db.close();
        return returnKVCacheItem;
    }


    /**
     * Delete key association cache item
     *
     * @param key
     */
    public void del(String key) {
        del(key, cacheDelListener);
    }

    public void del(String key, DelListener listener) {
        SQLiteDatabase db = sqLiteOpenHelper.getWritableDatabase();
        int effectRows = db.delete(MetaInfo.CACHE_TABLE_NAME, MetaInfo.KEY_COLUMN + "=?", new String[]{key});
        db.close();
        if (null != listener) {
            listener.onDelCallback(effectRows);
        }
    }


    /**
     * Clear all kv cache item
     */
    public int clear() {
        SQLiteDatabase db = sqLiteOpenHelper.getWritableDatabase();
        int effectRows = db.delete(MetaInfo.CACHE_TABLE_NAME, null, null);
        db.close();
        return effectRows;
    }

    /**
     * All kv cache item's size
     */
    public long size() {
        SQLiteDatabase db = sqLiteOpenHelper.getReadableDatabase();
        Cursor cursor = db.rawQuery("SELECT COUNT(1) as count FROM " + MetaInfo.CACHE_TABLE_NAME, new String[]{});
        if (cursor.moveToFirst()) {
            return cursor.getLong(cursor.getColumnIndex("count"));
        }
        cursor.close();
        db.close();
        return -1;
    }

    private long insert(SQLiteDatabase db, String tableName, String key, String value) {
        ContentValues cv = wrapperContentValues(0, key, value);
        long id = db.insert(tableName, null, cv);
        return id;
    }

    private int update(SQLiteDatabase db, String tableName, String key, String value) {
        ContentValues cv = wrapperContentValues(0, key, value);
        return db.update(tableName, cv, MetaInfo.KEY_COLUMN + "=?", new String[]{key});
    }

    private ContentValues wrapperContentValues(long _id, String key, String value) {
        ContentValues cv = new ContentValues(3);
        cv.put(MetaInfo.KEY_COLUMN, key);
        cv.put(MetaInfo.VALUE_COLUMN, value);
        if (_id != 0) {
            cv.put(MetaInfo.ID_COLUMN, _id);
        }
        return cv;
    }

    /**
     * Cache item entry object
     */
    public static class KVCacheItem implements Parcelable {

        public static final KVCacheItem NULL = new KVCacheItem(0, "", "");

        /**
         * Cache id's value in table
         */
        private long _id;

        /**
         * Cache key's value in table
         */
        private String key;

        /**
         * Cache value's value in table
         */
        private String value;

        public KVCacheItem(long _id, String key, String value) {
            this._id = _id;
            this.key = key;
            this.value = value;
        }

        public long get_id() {
            return _id;
        }

        public String getKey() {
            return key;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public String getValue() {
            return this.value;
        }

        /**
         * Cache item convert JSONObject
         *
         * @return JSONObject
         * @throws JSONException
         */
        public JSONObject toJSONObject() throws JSONException {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put(MetaInfo.ID_COLUMN, _id);
            jsonObject.put(MetaInfo.KEY_COLUMN, key);
            jsonObject.put(MetaInfo.VALUE_COLUMN, value);
            return jsonObject;
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeLong(_id);
            dest.writeString(key);
            dest.writeString(value);
        }

        public static final Creator CREATOR = new ClassLoaderCreator<KVCacheItem>() {

            @Override
            public KVCacheItem createFromParcel(Parcel source) {
                return new KVCacheItem(source.readLong(), source.readString(), source.readString());
            }

            @Override
            public KVCacheItem[] newArray(int size) {
                return new KVCacheItem[size];
            }

            @Override
            public KVCacheItem createFromParcel(Parcel source, ClassLoader loader) {
                return source.readParcelable(loader);
            }
        };
    }

    /**
     * Generate cache item's key interface
     */
    public interface CacheKeyGen {

        /**
         * Generate cache item's key
         *
         * @return cache item's key's value
         */
        String keyGen();
    }

    /**
     * Default cache item's key generator use random UUID
     */
    public static class DefaultCacheKeyGen implements CacheKeyGen {

        @Override
        public String keyGen() {
            return UUID.randomUUID().toString().replace("-", "");
        }
    }

    /**
     * Timestamp as cache item's key generator
     */
    public static class TimestampCacheKeyGen implements CacheKeyGen {

        @Override
        public String keyGen() {
            return String.valueOf(System.currentTimeMillis());
        }
    }


    /**
     * Cache put cache item callback listener
     */
    public interface PutListener {

        /**
         * Cache put event callback
         *
         * @param state          if state is true success , else fail
         * @param insertOrUpdate if insertOrUpdate is true insert, else update
         * @param KVCacheItem    if state is true return KVCacheItem(id,key,value), else KVCacheItem.NULL
         */
        void onPutCallback(boolean state, boolean insertOrUpdate, KVCacheItem KVCacheItem);
    }

    public interface DelListener {

        void onDelCallback(int effectRows);
    }

    /**
     * Cache get cache item callback listener
     */
    public interface GetListener {

        /**
         * Cache get event callback
         *
         * @param key cache item's key
         * @return cache item's value
         */
        String onGetCallback(String key);
    }
}
